/**
 * pharma-business - 1 juin 2011 guillaume
 */
package org.bspv.pharma.business.impl;

import java.util.Date;
import java.util.List;

import org.bspv.pharma.business.OrderManagementBusinessService;
import org.bspv.pharma.business.rules.order.OrderCancellationRule;
import org.bspv.pharma.business.rules.order.OrderCreationRule;
import org.bspv.pharma.business.rules.order.OrderDeletionRule;
import org.bspv.pharma.business.rules.order.OrderDeliveryEndingRule;
import org.bspv.pharma.business.rules.order.OrderDeliveryOpeningRule;
import org.bspv.pharma.business.rules.order.OrderDetailValidityRule;
import org.bspv.pharma.business.rules.order.OrderSendingRule;
import org.bspv.pharma.common.exception.ViolatedBusinessRuleException;
import org.bspv.pharma.manager.OrderCancellationManager;
import org.bspv.pharma.manager.OrderCreationManager;
import org.bspv.pharma.manager.OrderDeletionManager;
import org.bspv.pharma.manager.OrderDeliveryManager;
import org.bspv.pharma.manager.OrderDetailManager;
import org.bspv.pharma.manager.OrderManager;
import org.bspv.pharma.manager.OrderSendingManager;
import org.bspv.pharma.model.Order;
import org.bspv.pharma.model.Order.OrderStateEnum;
import org.bspv.pharma.model.OrderCancellation;
import org.bspv.pharma.model.OrderCreation;
import org.bspv.pharma.model.OrderDeletion;
import org.bspv.pharma.model.OrderDelivery;
import org.bspv.pharma.model.OrderDetail;
import org.bspv.pharma.model.OrderSending;
import org.bspv.pharma.model.TypeOfSupply;
import org.bspv.pharma.model.User;
import org.bspv.pharma.tools.annotations.specification.UseRG;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author guillaume
 * 
 */
@Service(value = "orderManagementBusinessService")
public class OrderManagementBusinessServiceImpl implements OrderManagementBusinessService {

    /**
     * Logger.
     */
    private final static Logger LOGGER = LoggerFactory.getLogger(OrderManagementBusinessServiceImpl.class);

    /** Service basique des commandes. */
    @Autowired
    private OrderManager orderManager;
    /** Service basique des détails de commandes. */
    @Autowired
    private OrderDetailManager orderDetailManager;
    /** Service basique des annulations de commandes. */
    @Autowired
    private OrderCancellationManager orderCancellationManager;
    /** Service basique des créations de commandes. */
    @Autowired
    private OrderCreationManager orderCreationManager;
    /** Service basique des suppressions de commandes. */
    @Autowired
    private OrderDeletionManager orderDeletionManager;
    /** Service basique des envois de commandes. */
    @Autowired
    private OrderSendingManager orderSendingManager;
    /** Service basique des livraisons de commandes. */
    @Autowired
    private OrderDeliveryManager orderDeliveryManager;

    // //////////////////////////////////////////////////////////////////////////////////////////////
    // METHODES METIER POUR LES COMMANDES //
    // //////////////////////////////////////////////////////////////////////////////////////////////

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_DELETION_001")
    @Override
    public void deleteOrder(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        Long id = order.getId();
        OrderDeletionRule rule = new OrderDeletionRule();
        if (rule.isSatisfiedBy(order)) {
            order.setDestructionDate(new Date());
            orderManager.save(order);
            // Evenement de suppression
            OrderDeletion deletion = new OrderDeletion();
            deletion.setOrder(order);
            deletion.setDate(new Date());
            deletion.setUser(user);
            deletion.setComment(comment);
            orderDeletionManager.save(deletion);
            LOGGER.info("Suppression de la commande numéro {}", id);
        } else {
            LOGGER.error("La commande numéro {} ne peut être supprimée", id);
            throw new ViolatedBusinessRuleException("La commande ne peut être supprimée");
        }
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_CANCELLATION_001")
    @Override
    public void cancelOrder(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        Long id = order.getId();
        OrderCancellationRule rule = new OrderCancellationRule();
        if (rule.isSatisfiedBy(order)) {
            order.setState(Order.OrderStateEnum.ANNULEE);
            order = orderManager.save(order);
            // Evenement d'annulation
            OrderCancellation cancellation = new OrderCancellation();
            cancellation.setOrder(order);
            cancellation.setDate(new Date());
            cancellation.setUser(user);
            cancellation.setComment(comment);
            orderCancellationManager.save(cancellation);
            LOGGER.info("Annulation de la commande numéro {}", id);
        } else {
            LOGGER.error("La commande numéro {} ne peut être annulée", id);
            throw new ViolatedBusinessRuleException("La commande ne peut être annulée");
        }
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_SENDING_001")
    @Override
    public void sendOrder(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        Long id = order.getId();
        OrderSendingRule rule = new OrderSendingRule();
        if (rule.isSatisfiedBy(order)) {
            // Evenement d'envoi
            OrderSending orderSending = new OrderSending();
            orderSending.setOrder(order);
            orderSending.setDate(new Date());
            orderSending.setUser(user);
            orderSending.setComment(comment);
            // Modification de l'état de la commande
            order.setState(OrderStateEnum.ENVOYEE);
            // Sauvegarde de l'évènement d'envoi et de la commande
            orderSendingManager.save(orderSending);
            orderManager.save(order);
            LOGGER.info("Envoi de la commande numéro {}", id);
        } else {
            LOGGER.error("La commande numéro {} ne peut être envoyée", id);
            throw new ViolatedBusinessRuleException("La commande ne peut être envoyée");
        }
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_DELIVERY_001")
    @Override
    public void beginOrderDelivery(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        Long id = order.getId();
        OrderDeliveryOpeningRule rule = new OrderDeliveryOpeningRule();
        if (rule.isSatisfiedBy(order)) {
            // Evenement de reception de commande
            OrderDelivery orderDelivery = new OrderDelivery();
            orderDelivery.setOrder(order);
            orderDelivery.setDate(new Date());
            orderDelivery.setUser(user);
            orderDelivery.setComment(comment);
            // Modification de l'état de la commande
            order.setState(OrderStateEnum.ENRECEPTION);
            // Sauvegarde de l'évènement d'envoi et de la commande
            orderDeliveryManager.save(orderDelivery);
            orderManager.save(order);
            LOGGER.info("Début de réception de la commande numéro {}", id);
        } else {
            LOGGER.error("La réception de la commande numéro {} est impossible", id);
            throw new ViolatedBusinessRuleException("La réception de la commande est impossible");
        }
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_DELIVERY_002")
    @Override
    public void completeOrderDelivery(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        Long id = order.getId();
        OrderDeliveryEndingRule rule = new OrderDeliveryEndingRule();
        if (rule.isSatisfiedBy(order)) {
            // Récupération de l'evenement de reception de commande
            OrderDelivery orderDelivery = orderDeliveryManager.findByOrder(order);
            orderDelivery.setEndOfDelivery(new Date());
            // Modification de l'état de la commande
            order.setState(OrderStateEnum.TERMINEE);
            // Sauvegarde de l'évènement d'envoi et de la commande
            orderDeliveryManager.save(orderDelivery);
            orderManager.save(order);
            LOGGER.info("Fin de réception de la commande numéro {}", id);
        } else {
            LOGGER.error("La cloture de la réception de la commande numéro {} est impossible", id);
            throw new ViolatedBusinessRuleException("La cloture de la réception de la commande est impossible");
        }
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_CREATION_001")
    @Override
    public Order createOrder(Order order, String comment, User user) throws ViolatedBusinessRuleException {
        // Vérification du respect des règles métier
        OrderCreationRule rule = new OrderCreationRule();
        if (rule.isSatisfiedBy(order)) {
            order.setState(Order.OrderStateEnum.ENCOURS);
            order = orderManager.save(order);
            // Evenement de création
            OrderCreation orderCreation = new OrderCreation();
            orderCreation.setOrder(order);
            orderCreation.setUser(user);
            orderCreation.setDate(new Date());
            orderCreation.setComment(comment);
            orderCreationManager.save(orderCreation);
            LOGGER.info("Création de la commande numéro " + order.getId());
        } else {
            LOGGER.error("Commande déjà en cours pour cette pharmacie");
            throw new ViolatedBusinessRuleException("La commande ne peut être créée");
        }
        return order;
    }

    /**
     * {@inheritDoc}
     */
    @UseRG(rg = "RG_ORDER_DETAIL_VALIDITY_001")
    @Override
    public void updateOrderDetail(Order order, TypeOfSupply typeOfSupply, Integer quantity)
            throws ViolatedBusinessRuleException {
        Long orderId = order.getId();
        Long typeOfsupplyId = typeOfSupply.getId();
        // Valeur par défaut pour la quantité en cas de nullité
        quantity = quantity == null ? 0 : quantity;
        // Récupération de la liste des détails de la commande
        List<OrderDetail> details = orderDetailManager.findDetails(order);
        OrderDetail orderDetail = null;
        // Si la quantité est égale à 0 alors on essaie de supprimer le détails de la commande
        if (quantity == 0) {
            // Recherche du détail dans la liste
            orderDetail = findDetail(details, typeOfSupply);
            // Suppression si détail trouvé
            if (orderDetail != null) {
                orderDetailManager.delete(orderDetail);
                LOGGER.info("Suppression du détail {} de la commande {}", typeOfsupplyId, orderId);
            }
        } else {
            // Recherche du détail dans la liste
            orderDetail = findDetail(details, typeOfSupply);
            // Si la quantité est différente de 0 et qu'aucun détail n'existe pour ce type de
            // fourniture alors on l'ajoute
            if (orderDetail == null) {
                orderDetail = new OrderDetail();
                orderDetail.setOrder(order);
                orderDetail.setTypeOfSupply(typeOfSupply);
                orderDetail.setQuantity(quantity);
            } else {
                // Si la quantité est différente de 0 et qu'un détail existe pour ce type de
                // fourniture alors on le met à jour
                orderDetail.setQuantity(quantity);
            }
            // Règle métier
            OrderDetailValidityRule orderDetailValidityRule = new OrderDetailValidityRule();
            if (orderDetailValidityRule.isSatisfiedBy(orderDetail)) {
                // Sauvegarde du detail
                orderDetail = orderDetailManager.save(orderDetail);
                LOGGER.info("Ajout/modification du détail {} à la commande {} ", orderDetail.getId(), orderId);
            } else {
                LOGGER.error("Erreur lors de l'ajout/modification d'un détail à la commande numéro {}", orderId);
                throw new ViolatedBusinessRuleException("Ajout/modification du détail impossible");
            }
        }
    }

    /**
     * Parcours une liste de détail de commande, si le détail concerne le type de fourniture en
     * paramètre, alors on le retourne.
     * 
     * @param orderDetails
     *            Liste de détails de commande
     * @param typeOfSupply
     *            Type de fourniture
     * @return Le détail de commande ou null
     */
    private OrderDetail findDetail(List<OrderDetail> orderDetails, TypeOfSupply typeOfSupply) {
        OrderDetail orderDetail = null;
        for (OrderDetail od : orderDetails) {
            if (typeOfSupply.equals(od.getTypeOfSupply())) {
                orderDetail = od;
            }
        }
        return orderDetail;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Order findOrder(Long id) {
        return orderManager.findById(id);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<OrderDetail> findOrderDetails(Order order) {
        return orderDetailManager.findDetails(order);
    }

}
